#include "visual_script_builtin_funcs.h"
#include "math_funcs.h"
#include "object_type_db.h"
#include "reference.h"
#include "func_ref.h"
#include "os/os.h"
#include "variant_parser.h"
#include "io/marshalls.h"

const char* VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX]={
	"sin",
	"cos",
	"tan",
	"sinh",
	"cosh",
	"tanh",
	"asin",
	"acos",
	"atan",
	"atan2",
	"sqrt",
	"fmod",
	"fposmod",
	"floor",
	"ceil",
	"round",
	"abs",
	"sign",
	"pow",
	"log",
	"exp",
	"is_nan",
	"is_inf",
	"ease",
	"decimals",
	"stepify",
	"lerp",
	"dectime",
	"randomize",
	"randi",
	"randf",
	"rand_range",
	"seed",
	"rand_seed",
	"deg2rad",
	"rad2deg",
	"linear2db",
	"db2linear",
	"max",
	"min",
	"clamp",
	"nearest_po2",
	"weakref",
	"funcref",
	"convert",
	"typeof",
	"type_exists",
	"char",
	"str",
	"print",
	"printerr",
	"printraw",
	"var2str",
	"str2var",
	"var2bytes",
	"bytes2var",
};

VisualScriptBuiltinFunc::BuiltinFunc VisualScriptBuiltinFunc::find_function(const String& p_string) {

	for(int i=0;i<FUNC_MAX;i++) {
		if (p_string==func_name[i])
			return BuiltinFunc(i);
	}

	return FUNC_MAX;
}

String VisualScriptBuiltinFunc::get_func_name(BuiltinFunc p_func) {

	ERR_FAIL_INDEX_V(p_func,FUNC_MAX,String());
	return func_name[p_func];
}


int VisualScriptBuiltinFunc::get_output_sequence_port_count() const {

	return has_input_sequence_port() ? 1 : 0;
}

bool VisualScriptBuiltinFunc::has_input_sequence_port() const{

	switch(func) {

		case MATH_RANDOMIZE:
		case TEXT_PRINT:
		case TEXT_PRINTERR:
		case TEXT_PRINTRAW:
			return true;
		default:
			return false;

	}

}

int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) {


	switch(p_func) {

		case MATH_RANDOMIZE:
		case MATH_RAND:
		case MATH_RANDF:
			return 0;
		case MATH_SIN:
		case MATH_COS:
		case MATH_TAN:
		case MATH_SINH:
		case MATH_COSH:
		case MATH_TANH:
		case MATH_ASIN:
		case MATH_ACOS:
		case MATH_ATAN:
		case MATH_SQRT:
		case MATH_FLOOR:
		case MATH_CEIL:
		case MATH_ROUND:
		case MATH_ABS:
		case MATH_SIGN:
		case MATH_LOG:
		case MATH_EXP:
		case MATH_ISNAN:
		case MATH_ISINF:
		case MATH_DECIMALS:
		case MATH_SEED:
		case MATH_RANDSEED:
		case MATH_DEG2RAD:
		case MATH_RAD2DEG:
		case MATH_LINEAR2DB:
		case MATH_DB2LINEAR:
		case LOGIC_NEAREST_PO2:
		case OBJ_WEAKREF:
		case TYPE_OF:
		case TEXT_CHAR:
		case TEXT_STR:
		case TEXT_PRINT:
		case TEXT_PRINTERR:
		case TEXT_PRINTRAW:
		case VAR_TO_STR:
		case STR_TO_VAR:
		case VAR_TO_BYTES:
		case BYTES_TO_VAR:
		case TYPE_EXISTS:
			return 1;
		case MATH_ATAN2:
		case MATH_FMOD:
		case MATH_FPOSMOD:
		case MATH_POW:
		case MATH_EASE:
		case MATH_STEPIFY:
		case MATH_RANDOM:
		case LOGIC_MAX:
		case LOGIC_MIN:
		case FUNC_FUNCREF:
		case TYPE_CONVERT:
			return 2;
		case MATH_LERP:
		case MATH_DECTIME:
		case LOGIC_CLAMP:
			return 3;
		case FUNC_MAX:{}

	}
	return 0;
}

int VisualScriptBuiltinFunc::get_input_value_port_count() const{

	return get_func_argument_count(func);
}
int VisualScriptBuiltinFunc::get_output_value_port_count() const{

	switch(func) {
		case MATH_RANDOMIZE:
		case TEXT_PRINT:
		case TEXT_PRINTERR:
		case TEXT_PRINTRAW:
		case MATH_SEED:
			return 0;
		case MATH_RANDSEED:
			return 2;
		default:
			return 1;
	}

	return 1;
}

String VisualScriptBuiltinFunc::get_output_sequence_port_text(int p_port) const {

	return String();
}

PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const{

	switch(func) {

		case MATH_SIN:
		case MATH_COS:
		case MATH_TAN:
		case MATH_SINH:
		case MATH_COSH:
		case MATH_TANH:
		case MATH_ASIN:
		case MATH_ACOS:
		case MATH_ATAN:
		case MATH_ATAN2:
		case MATH_SQRT: {
			return PropertyInfo(Variant::REAL,"num");
		} break;
		case MATH_FMOD:
		case MATH_FPOSMOD: {
			if (p_idx==0)
				return PropertyInfo(Variant::REAL,"x");
			else
				return PropertyInfo(Variant::REAL,"y");
		} break;
		case MATH_FLOOR:
		case MATH_CEIL:
		case MATH_ROUND:
		case MATH_ABS:
		case MATH_SIGN: {
			return PropertyInfo(Variant::REAL,"num");

		} break;

		case MATH_POW: {
			if (p_idx==0)
				return PropertyInfo(Variant::REAL,"x");
			else
				return PropertyInfo(Variant::REAL,"y");
		} break;
		case MATH_LOG:
		case MATH_EXP:
		case MATH_ISNAN:
		case MATH_ISINF: {
			return PropertyInfo(Variant::REAL,"num");
		} break;
		case MATH_EASE: {
			if (p_idx==0)
				return PropertyInfo(Variant::REAL,"s");
			else
				return PropertyInfo(Variant::REAL,"curve");
		} break;
		case MATH_DECIMALS: {
			return PropertyInfo(Variant::REAL,"step");
		} break;
		case MATH_STEPIFY: {
			if (p_idx==0)
				return PropertyInfo(Variant::REAL,"s");
			else
				return PropertyInfo(Variant::REAL,"steps");
		} break;
		case MATH_LERP: {
			if (p_idx==0)
				return PropertyInfo(Variant::REAL,"from");
			else if (p_idx==1)
				return PropertyInfo(Variant::REAL,"to");
			else
				return PropertyInfo(Variant::REAL,"weight");

		} break;
		case MATH_DECTIME: {
			if (p_idx==0)
				return PropertyInfo(Variant::REAL,"value");
			else if (p_idx==1)
				return PropertyInfo(Variant::REAL,"amount");
			else
				return PropertyInfo(Variant::REAL,"step");
		} break;
		case MATH_RANDOMIZE: {

		} break;
		case MATH_RAND: {

		} break;
		case MATH_RANDF: {

		} break;
		case MATH_RANDOM: {
			if (p_idx==0)
				return PropertyInfo(Variant::REAL,"from");
			else
				return PropertyInfo(Variant::REAL,"to");
		} break;
		case MATH_SEED: {
			return PropertyInfo(Variant::INT,"seed");
		} break;
		case MATH_RANDSEED: {
			return PropertyInfo(Variant::INT,"seed");
		} break;
		case MATH_DEG2RAD: {
			return PropertyInfo(Variant::REAL,"deg");
		} break;
		case MATH_RAD2DEG: {
			return PropertyInfo(Variant::REAL,"rad");
		} break;
		case MATH_LINEAR2DB: {
			return PropertyInfo(Variant::REAL,"nrg");
		} break;
		case MATH_DB2LINEAR: {
			return PropertyInfo(Variant::REAL,"db");
		} break;
		case LOGIC_MAX: {
			if (p_idx==0)
				return PropertyInfo(Variant::REAL,"a");
			else
				return PropertyInfo(Variant::REAL,"b");
		} break;
		case LOGIC_MIN: {
			if (p_idx==0)
				return PropertyInfo(Variant::REAL,"a");
			else
				return PropertyInfo(Variant::REAL,"b");
		} break;
		case LOGIC_CLAMP: {
			if (p_idx==0)
				return PropertyInfo(Variant::REAL,"a");
			else if (p_idx==0)
				return PropertyInfo(Variant::REAL,"min");
			else
				return PropertyInfo(Variant::REAL,"max");
		} break;
		case LOGIC_NEAREST_PO2: {
			return PropertyInfo(Variant::INT,"num");
		} break;
		case OBJ_WEAKREF: {

			return PropertyInfo(Variant::OBJECT,"source");

		} break;
		case FUNC_FUNCREF: {

			if (p_idx==0)
				return PropertyInfo(Variant::OBJECT,"instance");
			else
				return PropertyInfo(Variant::STRING,"funcname");

		} break;
		case TYPE_CONVERT: {

			if (p_idx==0)
				return PropertyInfo(Variant::NIL,"what");
			else
				return PropertyInfo(Variant::STRING,"type");
		} break;
		case TYPE_OF: {
			return PropertyInfo(Variant::NIL,"what");

		} break;
		case TYPE_EXISTS: {

			return PropertyInfo(Variant::STRING,"type");

		} break;
		case TEXT_CHAR: {

			return PropertyInfo(Variant::INT,"ascii");


		} break;
		case TEXT_STR: {

			return PropertyInfo(Variant::NIL,"value");


		} break;
		case TEXT_PRINT: {

			return PropertyInfo(Variant::NIL,"value");

		} break;
		case TEXT_PRINTERR: {
			return PropertyInfo(Variant::NIL,"value");

		} break;
		case TEXT_PRINTRAW: {

			return PropertyInfo(Variant::NIL,"value");

		} break;
		case VAR_TO_STR: {
			return PropertyInfo(Variant::NIL,"var");

		} break;
		case STR_TO_VAR: {

			return PropertyInfo(Variant::STRING,"string");
		} break;
		case VAR_TO_BYTES: {
			return PropertyInfo(Variant::NIL,"var");

		} break;
		case BYTES_TO_VAR: {

			return PropertyInfo(Variant::RAW_ARRAY,"bytes");
		} break;
		case FUNC_MAX:{}
	}

	return PropertyInfo();
}

PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) const{

	Variant::Type t=Variant::NIL;
	switch(func) {

		case MATH_SIN:
		case MATH_COS:
		case MATH_TAN:
		case MATH_SINH:
		case MATH_COSH:
		case MATH_TANH:
		case MATH_ASIN:
		case MATH_ACOS:
		case MATH_ATAN:
		case MATH_ATAN2:
		case MATH_SQRT:
		case MATH_FMOD:
		case MATH_FPOSMOD:
		case MATH_FLOOR:
		case MATH_CEIL: {
			t=Variant::REAL;
		} break;
		case MATH_ROUND: {
			t=Variant::INT;
		} break;
		case MATH_ABS: {
			t=Variant::NIL;
		} break;
		case MATH_SIGN: {
			t=Variant::NIL;
		} break;
		case MATH_POW:
		case MATH_LOG:
		case MATH_EXP: {
			t=Variant::REAL;
		} break;
		case MATH_ISNAN:
		case MATH_ISINF: {
			t=Variant::BOOL;
		} break;
		case MATH_EASE: {
			t=Variant::REAL;
		} break;
		case MATH_DECIMALS: {
			t=Variant::INT;
		} break;
		case MATH_STEPIFY:
		case MATH_LERP:
		case MATH_DECTIME: {
			t=Variant::REAL;

		} break;
		case MATH_RANDOMIZE: {

		} break;
		case MATH_RAND: {

			t=Variant::INT;
		} break;
		case MATH_RANDF:
		case MATH_RANDOM: {
			t=Variant::REAL;
		} break;
		case MATH_SEED: {

		} break;
		case MATH_RANDSEED: {

			if (p_idx==0)
				return PropertyInfo(Variant::INT,"rnd");
			else
				return PropertyInfo(Variant::INT,"seed");
		} break;
		case MATH_DEG2RAD:
		case MATH_RAD2DEG:
		case MATH_LINEAR2DB:
		case MATH_DB2LINEAR: {
			t=Variant::REAL;
		} break;
		case LOGIC_MAX:
		case LOGIC_MIN:
		case LOGIC_CLAMP: {


		} break;

		case LOGIC_NEAREST_PO2: {
			t=Variant::NIL;
		} break;
		case OBJ_WEAKREF: {

			t=Variant::OBJECT;

		} break;
		case FUNC_FUNCREF: {

			t=Variant::OBJECT;

		} break;
		case TYPE_CONVERT: {



		} break;
		case TYPE_OF: {
			t=Variant::INT;

		} break;
		case TYPE_EXISTS: {

			t=Variant::BOOL;

		} break;
		case TEXT_CHAR:
		case TEXT_STR: {

			t=Variant::STRING;

		} break;
		case TEXT_PRINT: {


		} break;
		case TEXT_PRINTERR: {

		} break;
		case TEXT_PRINTRAW: {

		} break;
		case VAR_TO_STR: {
			t=Variant::STRING;
		} break;
		case STR_TO_VAR: {

		} break;
		case VAR_TO_BYTES: {
			t=Variant::RAW_ARRAY;

		} break;
		case BYTES_TO_VAR: {


		} break;
		case FUNC_MAX:{}
	}

	return PropertyInfo(t,"");
}

String VisualScriptBuiltinFunc::get_caption() const {

	return "BuiltinFunc";
}

String VisualScriptBuiltinFunc::get_text() const {

	return func_name[func];
}

void VisualScriptBuiltinFunc::set_func(BuiltinFunc p_which) {

	ERR_FAIL_INDEX(p_which,FUNC_MAX);
	func=p_which;
	_change_notify();
	ports_changed_notify();
}

VisualScriptBuiltinFunc::BuiltinFunc VisualScriptBuiltinFunc::get_func() {
	return func;
}


#define VALIDATE_ARG_NUM(m_arg) \
	if (!p_inputs[m_arg]->is_num()) {\
		r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;\
		r_error.argument=m_arg;\
		r_error.expected=Variant::REAL;\
		return;\
	}


void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func,const Variant** p_inputs,Variant* r_return,Variant::CallError& r_error,String& r_error_str) {

	switch(p_func) {
		case VisualScriptBuiltinFunc::MATH_SIN: {

			VALIDATE_ARG_NUM(0);
			*r_return=Math::sin(*p_inputs[0]);
		} break;
		case VisualScriptBuiltinFunc::MATH_COS: {

			VALIDATE_ARG_NUM(0);
			*r_return=Math::cos(*p_inputs[0]);
		} break;
		case VisualScriptBuiltinFunc::MATH_TAN: {

			VALIDATE_ARG_NUM(0);
			*r_return=Math::tan(*p_inputs[0]);
		} break;
		case VisualScriptBuiltinFunc::MATH_SINH: {

			VALIDATE_ARG_NUM(0);
			*r_return=Math::sinh(*p_inputs[0]);
		} break;
		case VisualScriptBuiltinFunc::MATH_COSH: {

			VALIDATE_ARG_NUM(0);
			*r_return=Math::cosh(*p_inputs[0]);
		} break;
		case VisualScriptBuiltinFunc::MATH_TANH: {

			VALIDATE_ARG_NUM(0);
			*r_return=Math::tanh(*p_inputs[0]);
		} break;
		case VisualScriptBuiltinFunc::MATH_ASIN: {

			VALIDATE_ARG_NUM(0);
			*r_return=Math::asin(*p_inputs[0]);
		} break;
		case VisualScriptBuiltinFunc::MATH_ACOS: {

			VALIDATE_ARG_NUM(0);
			*r_return=Math::acos(*p_inputs[0]);
		} break;
		case VisualScriptBuiltinFunc::MATH_ATAN: {

			VALIDATE_ARG_NUM(0);
			*r_return=Math::atan(*p_inputs[0]);
		} break;
		case VisualScriptBuiltinFunc::MATH_ATAN2: {

			VALIDATE_ARG_NUM(0);
			VALIDATE_ARG_NUM(1);
			*r_return=Math::atan2(*p_inputs[0],*p_inputs[1]);
		} break;
		case VisualScriptBuiltinFunc::MATH_SQRT: {

			VALIDATE_ARG_NUM(0);
			*r_return=Math::sqrt(*p_inputs[0]);
		} break;
		case VisualScriptBuiltinFunc::MATH_FMOD: {

			VALIDATE_ARG_NUM(0);
			VALIDATE_ARG_NUM(1);
			*r_return=Math::fmod(*p_inputs[0],*p_inputs[1]);
		} break;
		case VisualScriptBuiltinFunc::MATH_FPOSMOD: {

			VALIDATE_ARG_NUM(0);
			VALIDATE_ARG_NUM(1);
			*r_return=Math::fposmod(*p_inputs[0],*p_inputs[1]);
		} break;
		case VisualScriptBuiltinFunc::MATH_FLOOR: {

			VALIDATE_ARG_NUM(0);
			*r_return=Math::floor(*p_inputs[0]);
		  } break;
		case VisualScriptBuiltinFunc::MATH_CEIL: {

			VALIDATE_ARG_NUM(0);
			*r_return=Math::ceil(*p_inputs[0]);
		} break;
		case VisualScriptBuiltinFunc::MATH_ROUND: {

			VALIDATE_ARG_NUM(0);
			*r_return=Math::round(*p_inputs[0]);
		} break;
		case VisualScriptBuiltinFunc::MATH_ABS: {

			if (p_inputs[0]->get_type()==Variant::INT) {

				int64_t i = *p_inputs[0];
				*r_return=ABS(i);
			} else if (p_inputs[0]->get_type()==Variant::REAL) {

				real_t r = *p_inputs[0];
				*r_return=Math::abs(r);
			} else {

				r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
				r_error.argument=0;
				r_error.expected=Variant::REAL;

			}
		} break;
		case VisualScriptBuiltinFunc::MATH_SIGN: {

				if (p_inputs[0]->get_type()==Variant::INT) {

					int64_t i = *p_inputs[0];
					*r_return= i < 0 ? -1 : ( i > 0 ? +1 : 0);
				} else if (p_inputs[0]->get_type()==Variant::REAL) {

					real_t r = *p_inputs[0];
					*r_return= r < 0.0 ? -1.0 : ( r > 0.0 ? +1.0 : 0.0);
				} else {

					r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
					r_error.argument=0;
					r_error.expected=Variant::REAL;

				}
		} break;
		case VisualScriptBuiltinFunc::MATH_POW: {

			VALIDATE_ARG_NUM(0);
			VALIDATE_ARG_NUM(1);
			*r_return=Math::pow(*p_inputs[0],*p_inputs[1]);
		} break;
		case VisualScriptBuiltinFunc::MATH_LOG: {

			VALIDATE_ARG_NUM(0);
			*r_return=Math::log(*p_inputs[0]);
		} break;
		case VisualScriptBuiltinFunc::MATH_EXP: {

			VALIDATE_ARG_NUM(0);
			*r_return=Math::exp(*p_inputs[0]);
		} break;
		case VisualScriptBuiltinFunc::MATH_ISNAN: {

			VALIDATE_ARG_NUM(0);
			*r_return=Math::is_nan(*p_inputs[0]);
		} break;
		case VisualScriptBuiltinFunc::MATH_ISINF: {

			VALIDATE_ARG_NUM(0);
			*r_return=Math::is_inf(*p_inputs[0]);
		} break;
		case VisualScriptBuiltinFunc::MATH_EASE: {

			VALIDATE_ARG_NUM(0);
			VALIDATE_ARG_NUM(1);
			*r_return=Math::ease(*p_inputs[0],*p_inputs[1]);
		} break;
		case VisualScriptBuiltinFunc::MATH_DECIMALS: {

			VALIDATE_ARG_NUM(0);
			*r_return=Math::step_decimals(*p_inputs[0]);
		} break;
		case VisualScriptBuiltinFunc::MATH_STEPIFY: {

			VALIDATE_ARG_NUM(0);
			VALIDATE_ARG_NUM(1);
			*r_return=Math::stepify(*p_inputs[0],*p_inputs[1]);
		} break;
		case VisualScriptBuiltinFunc::MATH_LERP: {

			VALIDATE_ARG_NUM(0);
			VALIDATE_ARG_NUM(1);
			VALIDATE_ARG_NUM(2);
			*r_return=Math::lerp(*p_inputs[0],*p_inputs[1],*p_inputs[2]);
		} break;
		case VisualScriptBuiltinFunc::MATH_DECTIME: {

			VALIDATE_ARG_NUM(0);
			VALIDATE_ARG_NUM(1);
			VALIDATE_ARG_NUM(2);
			*r_return=Math::dectime(*p_inputs[0],*p_inputs[1],*p_inputs[2]);
		} break;
		case VisualScriptBuiltinFunc::MATH_RANDOMIZE: {
			Math::randomize();

		} break;
		case VisualScriptBuiltinFunc::MATH_RAND: {
			*r_return=Math::rand();
		} break;
		case VisualScriptBuiltinFunc::MATH_RANDF: {
			*r_return=Math::randf();
		} break;
		case VisualScriptBuiltinFunc::MATH_RANDOM: {

			VALIDATE_ARG_NUM(0);
			VALIDATE_ARG_NUM(1);
			*r_return=Math::random(*p_inputs[0],*p_inputs[1]);
		} break;
		case VisualScriptBuiltinFunc::MATH_SEED: {

			VALIDATE_ARG_NUM(0);
			uint32_t seed=*p_inputs[0];
			Math::seed(seed);

		} break;
		case VisualScriptBuiltinFunc::MATH_RANDSEED: {

			VALIDATE_ARG_NUM(0);
			uint32_t seed=*p_inputs[0];
			int ret = Math::rand_from_seed(&seed);
			Array reta;
			reta.push_back(ret);
			reta.push_back(seed);
			*r_return=reta;

		} break;
		case VisualScriptBuiltinFunc::MATH_DEG2RAD: {

			VALIDATE_ARG_NUM(0);
			*r_return=Math::deg2rad(*p_inputs[0]);
		} break;
		case VisualScriptBuiltinFunc::MATH_RAD2DEG: {

			VALIDATE_ARG_NUM(0);
			*r_return=Math::rad2deg(*p_inputs[0]);
		} break;
		case VisualScriptBuiltinFunc::MATH_LINEAR2DB: {

			VALIDATE_ARG_NUM(0);
			*r_return=Math::linear2db(*p_inputs[0]);
		} break;
		case VisualScriptBuiltinFunc::MATH_DB2LINEAR: {

			VALIDATE_ARG_NUM(0);
			*r_return=Math::db2linear(*p_inputs[0]);
		} break;
		case VisualScriptBuiltinFunc::LOGIC_MAX: {

			if (p_inputs[0]->get_type()==Variant::INT && p_inputs[1]->get_type()==Variant::INT) {

				int64_t a = *p_inputs[0];
				int64_t b = *p_inputs[1];
				*r_return=MAX(a,b);
			} else {
				VALIDATE_ARG_NUM(0);
				VALIDATE_ARG_NUM(1);

				real_t a = *p_inputs[0];
				real_t b = *p_inputs[1];

				*r_return=MAX(a,b);
			}

		} break;
		case VisualScriptBuiltinFunc::LOGIC_MIN: {

			if (p_inputs[0]->get_type()==Variant::INT && p_inputs[1]->get_type()==Variant::INT) {

				int64_t a = *p_inputs[0];
				int64_t b = *p_inputs[1];
				*r_return=MIN(a,b);
			} else {
				VALIDATE_ARG_NUM(0);
				VALIDATE_ARG_NUM(1);

				real_t a = *p_inputs[0];
				real_t b = *p_inputs[1];

				*r_return=MIN(a,b);
			}
		} break;
		case VisualScriptBuiltinFunc::LOGIC_CLAMP: {

			if (p_inputs[0]->get_type()==Variant::INT && p_inputs[1]->get_type()==Variant::INT && p_inputs[2]->get_type()==Variant::INT) {

				int64_t a = *p_inputs[0];
				int64_t b = *p_inputs[1];
				int64_t c = *p_inputs[2];
				*r_return=CLAMP(a,b,c);
			} else {
				VALIDATE_ARG_NUM(0);
				VALIDATE_ARG_NUM(1);
				VALIDATE_ARG_NUM(2);

				real_t a = *p_inputs[0];
				real_t b = *p_inputs[1];
				real_t c = *p_inputs[2];

				*r_return=CLAMP(a,b,c);
			}
		} break;
		case VisualScriptBuiltinFunc::LOGIC_NEAREST_PO2: {

			VALIDATE_ARG_NUM(0);
			int64_t num = *p_inputs[0];
			*r_return = nearest_power_of_2(num);
		} break;
		case VisualScriptBuiltinFunc::OBJ_WEAKREF: {

			if (p_inputs[0]->get_type()!=Variant::OBJECT) {

				r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
				r_error.argument=0;
				r_error.expected=Variant::OBJECT;

				return;

			}

			if (p_inputs[0]->is_ref()) {

				REF r = *p_inputs[0];
				if (!r.is_valid()) {

					return;
				}

				Ref<WeakRef> wref = memnew( WeakRef );
				wref->set_ref(r);
				*r_return=wref;
			} else {
				Object *obj = *p_inputs[0];
				if (!obj) {

					return;
				}
				Ref<WeakRef> wref = memnew( WeakRef );
				wref->set_obj(obj);
				*r_return=wref;
			}




		} break;
		case VisualScriptBuiltinFunc::FUNC_FUNCREF: {

			if (p_inputs[0]->get_type()!=Variant::OBJECT) {

				r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
				r_error.argument=0;
				r_error.expected=Variant::OBJECT;

				return;

			}
			if (p_inputs[1]->get_type()!=Variant::STRING && p_inputs[1]->get_type()!=Variant::NODE_PATH) {

				r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
				r_error.argument=1;
				r_error.expected=Variant::STRING;

				return;

			}

			Ref<FuncRef> fr = memnew( FuncRef);

			fr->set_instance(*p_inputs[0]);
			fr->set_function(*p_inputs[1]);

			*r_return=fr;

		} break;
		case VisualScriptBuiltinFunc::TYPE_CONVERT: {

			VALIDATE_ARG_NUM(1);
			int type=*p_inputs[1];
			if (type<0 || type>=Variant::VARIANT_MAX) {

				r_error_str=RTR("Invalid type argument to convert(), use TYPE_* constants.");
				r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
				r_error.argument=0;
				r_error.expected=Variant::INT;
				return;

			} else {


				*r_return=Variant::construct(Variant::Type(type),p_inputs,1,r_error);
			}
		} break;
		case VisualScriptBuiltinFunc::TYPE_OF: {


			*r_return = p_inputs[0]->get_type();

		} break;
		case VisualScriptBuiltinFunc::TYPE_EXISTS: {


			*r_return = ClassDB::class_exists(*p_inputs[0]);

		} break;
		case VisualScriptBuiltinFunc::TEXT_CHAR: {

			CharType result[2] = {*p_inputs[0], 0};
			
			*r_return=String(result);

		} break;
		case VisualScriptBuiltinFunc::TEXT_STR: {

			String str = *p_inputs[0];

			*r_return=str;

		} break;
		case VisualScriptBuiltinFunc::TEXT_PRINT: {

			String str = *p_inputs[0];
			print_line(str);


		} break;

		case VisualScriptBuiltinFunc::TEXT_PRINTERR: {

			String str = *p_inputs[0];

			//str+="\n";
			OS::get_singleton()->printerr("%s\n",str.utf8().get_data());


		} break;
		case VisualScriptBuiltinFunc::TEXT_PRINTRAW: {
			String str = *p_inputs[0];

			//str+="\n";
			OS::get_singleton()->print("%s",str.utf8().get_data());


		} break;
		case VisualScriptBuiltinFunc::VAR_TO_STR: {

			String vars;
			VariantWriter::write_to_string(*p_inputs[0],vars);
			*r_return=vars;
		} break;
		case VisualScriptBuiltinFunc::STR_TO_VAR: {

			if (p_inputs[0]->get_type()!=Variant::STRING) {
				r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
				r_error.argument=0;
				r_error.expected=Variant::STRING;

				return;
			}

			VariantParser::StreamString ss;
			ss.s=*p_inputs[0];

			String errs;
			int line;
			Error err = VariantParser::parse(&ss,*r_return,errs,line);

			if (err!=OK) {
				r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
				r_error.argument=0;
				r_error.expected=Variant::STRING;
				*r_return="Parse error at line "+itos(line)+": "+errs;
				return;
			}

		} break;
		case VisualScriptBuiltinFunc::VAR_TO_BYTES: {


			ByteArray barr;
			int len;
			Error err = encode_variant(*p_inputs[0],NULL,len);
			if (err) {
				r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
				r_error.argument=0;
				r_error.expected=Variant::NIL;
				r_error_str="Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID).";
				return;
			}

			barr.resize(len);
			{
				ByteArray::Write w = barr.write();
				encode_variant(*p_inputs[0],w.ptr(),len);

			}
			*r_return=barr;
		} break;
		case VisualScriptBuiltinFunc::BYTES_TO_VAR: {

			if (p_inputs[0]->get_type()!=Variant::RAW_ARRAY) {
				r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
				r_error.argument=0;
				r_error.expected=Variant::RAW_ARRAY;

				return;
			}

			ByteArray varr=*p_inputs[0];
			Variant ret;
			{
				ByteArray::Read r=varr.read();
				Error err = decode_variant(ret,r.ptr(),varr.size(),NULL);
				if (err!=OK) {
					r_error_str=RTR("Not enough bytes for decoding bytes, or invalid format.");
					r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
					r_error.argument=0;
					r_error.expected=Variant::RAW_ARRAY;
					return;
				}

			}

			*r_return=ret;

		} break;
		default: {}
	}

}


class VisualScriptNodeInstanceBuiltinFunc : public VisualScriptNodeInstance {
public:

	VisualScriptBuiltinFunc *node;
	VisualScriptInstance *instance;

	VisualScriptBuiltinFunc::BuiltinFunc func;


	//virtual int get_working_memory_size() const { return 0; }
	//virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
	//virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }

	virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {

		VisualScriptBuiltinFunc::exec_func(func,p_inputs,p_outputs[0],r_error,r_error_str);
		return 0;
	}


};

VisualScriptNodeInstance* VisualScriptBuiltinFunc::instance(VisualScriptInstance* p_instance) {

	VisualScriptNodeInstanceBuiltinFunc * instance = memnew(VisualScriptNodeInstanceBuiltinFunc );
	instance->node=this;
	instance->instance=p_instance;
	instance->func=func;
	return instance;
}


void VisualScriptBuiltinFunc::_bind_methods() {

	ClassDB::bind_method(_MD("set_func","which"),&VisualScriptBuiltinFunc::set_func);
	ClassDB::bind_method(_MD("get_func"),&VisualScriptBuiltinFunc::get_func);

	String cc;

	for(int i=0;i<FUNC_MAX;i++) {

		if (i>0)
			cc+=",";
		cc+=func_name[i];
	}
	ADD_PROPERTY(PropertyInfo(Variant::INT,"function",PROPERTY_HINT_ENUM,cc),_SCS("set_func"),_SCS("get_func"));
}

VisualScriptBuiltinFunc::VisualScriptBuiltinFunc() {

	func=MATH_SIN;
}

template<VisualScriptBuiltinFunc::BuiltinFunc func>
static Ref<VisualScriptNode> create_builtin_func_node(const String& p_name) {

	Ref<VisualScriptBuiltinFunc> node;
	node.instance();
	node->set_func(func);
	return node;
}

void register_visual_script_builtin_func_node() {


	VisualScriptLanguage::singleton->add_register_func("functions/built_in/sin",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SIN>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/cos",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_COS>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/tan",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_TAN>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/sinh",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SINH>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/cosh",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_COSH>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/tanh",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_TANH>);

	VisualScriptLanguage::singleton->add_register_func("functions/built_in/asin",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ASIN>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/acos",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ACOS>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/atan",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ATAN>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/atan2",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ATAN2>);

	VisualScriptLanguage::singleton->add_register_func("functions/built_in/sqrt",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SQRT>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/fmod",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_FMOD>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/fposmod",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_FPOSMOD>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/floor",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_FLOOR>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/ceil",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_CEIL>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/round",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ROUND>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/abs",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ABS>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/sign",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SIGN>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/pow",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_POW>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/log",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LOG>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/exp",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_EXP>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/isnan",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ISNAN>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/isinf",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ISINF>);

	VisualScriptLanguage::singleton->add_register_func("functions/built_in/ease",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_EASE>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/decimals",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DECIMALS>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/stepify",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_STEPIFY>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/lerp",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LERP>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/dectime",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DECTIME>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/randomize",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDOMIZE>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/rand",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RAND>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/randf",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDF>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/random",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDOM>);

	VisualScriptLanguage::singleton->add_register_func("functions/built_in/seed",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SEED>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/randseed",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDSEED>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/deg2rad",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DEG2RAD>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/rad2deg",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RAD2DEG>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/linear2db",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LINEAR2DB>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/db2linear",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DB2LINEAR>);

	VisualScriptLanguage::singleton->add_register_func("functions/built_in/max",create_builtin_func_node<VisualScriptBuiltinFunc::LOGIC_MAX>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/min",create_builtin_func_node<VisualScriptBuiltinFunc::LOGIC_MIN>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/clamp",create_builtin_func_node<VisualScriptBuiltinFunc::LOGIC_CLAMP>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/nearest_po2",create_builtin_func_node<VisualScriptBuiltinFunc::LOGIC_NEAREST_PO2>);

	VisualScriptLanguage::singleton->add_register_func("functions/built_in/weakref",create_builtin_func_node<VisualScriptBuiltinFunc::OBJ_WEAKREF>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/funcref",create_builtin_func_node<VisualScriptBuiltinFunc::FUNC_FUNCREF>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/convert",create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_CONVERT>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/typeof",create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_OF>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/type_exists",create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_EXISTS>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/char",create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_CHAR>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/str",create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_STR>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/print",create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_PRINT>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/printerr",create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_PRINTERR>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/printraw",create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_PRINTRAW>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/var2str",create_builtin_func_node<VisualScriptBuiltinFunc::VAR_TO_STR>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/str2var",create_builtin_func_node<VisualScriptBuiltinFunc::STR_TO_VAR>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/var2bytes",create_builtin_func_node<VisualScriptBuiltinFunc::VAR_TO_BYTES>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/bytes2var",create_builtin_func_node<VisualScriptBuiltinFunc::BYTES_TO_VAR>);

}
